#include "CryptoUtil.h"
#include <cstring>
#include <sstream>
#include <openssl/sha.h>
#include <iostream>

using namespace std;

// 定义SECRET_KEY,8位
static const char *SECRET_KEY = "*$5GTM#BF"; // 内部链接，外部不可见，其他编译单元看不见

// 1.生成随机字符串，用于盐值或Token
string CryptoUtil::generate_salt(int length)
{
    char *alpha = "0123456789"
                  "abcdefghijklmnopqrstuvwxyz"
                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int alpha_len = strlen(alpha);

    string result;
    for (int i = 0; i < length; i++)
    {
        result += alpha[rand() % alpha_len];
    }
    return result;
}

string CryptoUtil::hash_password(const string &password, const string &salt, const EVP_MD *md)
{
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();  // 创建 EVP 上下文
    unsigned char hash[EVP_MAX_MD_SIZE]; // EVP_MAX_MD_SIZE: 最大哈希长度
    unsigned int hash_len;               // 用来接收实际的哈希长度

    EVP_DigestInit_ex(ctx, md, NULL);                         // 初始化上下文，采用 sha256 哈希算法
    EVP_DigestUpdate(ctx, password.c_str(), password.size()); // 更新上下文
    EVP_DigestUpdate(ctx, salt.c_str(), salt.size());         // 把密码和盐值都加入，计算加盐哈希-注入数据库
    EVP_DigestFinal_ex(ctx, hash, &hash_len);                 // 计算哈希值

    char result[2 * EVP_MAX_MD_SIZE + 1] = {'\0'}; // 初始化为0值
    for (int i = 0; i < hash_len; i++)
    { // 转换成十六进制字符
        sprintf(result + 2 * i, "%02x", hash[i]);
    }

    EVP_MD_CTX_free(ctx); // 释放上下文
    return result;
}

string CryptoUtil::generate_token(const User &user, jwt_alg_t algorithm)
{
    jwt_t *jwt;
    jwt_new(&jwt);

    // 设置算法
    jwt_set_alg(jwt, algorithm, (unsigned char *)SECRET_KEY, strlen(SECRET_KEY));

    // 添加载荷payload:用户自定义数据，不能存放密码和盐值
    jwt_add_grant(jwt, "sub", "login");                    // 主题
    jwt_add_grant_int(jwt, "id", user.id);                 // 用户ID
    jwt_add_grant(jwt, "username", user.username.c_str()); // 用户名字

    jwt_add_grant_int(jwt, "exp", time(NULL) + 3600); // 过期时间：1h

    // 计算Token
    char *Token = jwt_encode_str(jwt);

    string result = Token;

    // 释放资源
    jwt_free(jwt);
    free(Token);

    return result;
}

bool CryptoUtil::verify_token(const string &token, User &user)
{
    jwt_t *jwt;
    int err = jwt_decode(&jwt, token.c_str(), (unsigned char *)SECRET_KEY, strlen(SECRET_KEY));
    if (err)
    {
        cerr << "Invalid JWT!" << endl;
        return false;
    }

    // 验证主题
    if (strcmp(jwt_get_grant(jwt, "sub"), "login"))
    {
        cerr << "Invalid JWT subject!" << endl;
        jwt_free(jwt);
        return false;
    }

    // 判断是否过期
    if (jwt_get_grant_int(jwt, "exp") < time(NULL))
    {
        cerr << "JWT expired!" << endl;
        jwt_free(jwt);
        return false;
    }

    // 解析token，获取信息，给传出参数user
    user.id = jwt_get_grant_int(jwt, "id");
    user.username = jwt_get_grant(jwt, "username");

    // 释放资源
    jwt_free(jwt);

    return true;
}

string CryptoUtil::hash_file(const string &filename)
{
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();  // 创建 EVP 上下文
    unsigned char hash[EVP_MAX_MD_SIZE]; // EVP_MAX_MD_SIZE: 最大哈希长度
    unsigned int hash_len;               // 用来接收实际的哈希长度

    // 初始化上下文，使用 SHA-256 算法
    EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
    // 更新上下文，输入文件内容
    EVP_DigestUpdate(ctx, filename.c_str(), filename.length());
    // 计算哈希值
    EVP_DigestFinal_ex(ctx, hash, &hash_len);

    char result[2 * EVP_MAX_MD_SIZE + 1] = {'\0'}; // 初始化为0值
    for (int i = 0; i < hash_len; i++)
    { // 转换成十六进制字符
        sprintf(result + 2 * i, "%02x", hash[i]);
    }

    EVP_MD_CTX_free(ctx); // 释放上下文
    return result;
}
